package com.hihuzi.test.util;


import com.hihuzi.test.Demo.FSupplier;

import java.io.*;
import java.util.Objects;

/**
 * 这个类是从 {@link java.lang.invoke.SerializedLambda} 里面 copy 过来的，
 * 字段信息完全一样
 * <p>负责将一个支持序列的 Function 序列化为 SerializedLambda</p>
 *
 * @author HCL
 * @since 2018/05/10
 */
@SuppressWarnings("unused")
public class LambdaUtil implements Serializable {

    /**
     * 通过反序列化转换 lambda 表达式，该方法只能序列化 lambda 表达式，不能序列化接口实现或者正常非 lambda 写法的对象
     *
     * @param lambda lambda对象
     * @return 返回解析后的 SerializedLambda
     * @deprecated 3.4.2 {@link #resolve(SFunction, ClassLoader)}
     */
    public static SerializedLambda resolve(SFunction<?, ?> lambda) {
        return resolve(lambda, null);
    }

    public static SerializedLambda resolve(SFunction<?, ?> lambda, ClassLoader classLoader) {
        if (!lambda.getClass().isSynthetic()) {
            assert Objects.isNull("该方法仅能传入 lambda 表达式产生的合成类");
        }
        try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(serialize(lambda))) {
            Class<?> clazz;

            @Override
            protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
                try {
                    clazz = ClassUtils.toClassConfident(objectStreamClass.getName(), classLoader);
                } catch (Exception ex) {
                    clazz = super.resolveClass(objectStreamClass);
                }
                return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
            }
        }) {
            return (SerializedLambda) objIn.readObject();
        } catch (ClassNotFoundException | IOException e) {
        }
        return null;
    }

    public static byte[] serialize(Object object) {
        if (object == null) {
            return null;
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(object);
            oos.flush();
        } catch (IOException ex) {
            throw new IllegalArgumentException("Failed to serialize object of type: " + object.getClass(), ex);
        }
        return baos.toByteArray();
    }

    public static <T> T process(FSupplier<T> supplier) {
        com.hihuzi.test.util.SerializedLambda lambda = null;
        try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(serialize(supplier))) {
            @Override
            protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
                Class<?> clazz;
                try {
                    clazz =  ClassUtils.toClassConfident(objectStreamClass.getName(), null);
                } catch (Exception ex) {
                    clazz = super.resolveClass(objectStreamClass);
                }
                return clazz == java.lang.invoke.SerializedLambda.class ? com.hihuzi.test.util.SerializedLambda.class : clazz;
            }
        }) {
            lambda = (com.hihuzi.test.util.SerializedLambda) objIn.readObject();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
        System.out.println(lambda);
        return supplier.get();
    }
}
